/*
 * Wazuh Vulnerability Scanner - Unit Tests
 * Copyright (C) 2015, Wazuh Inc.
 * September 21, 2023.
 *
 * This program is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public
 * License (version 2) as published by the FSF - Free Software
 * Foundation.
 */

#include "eventInsertInventory_test.hpp"
#include "../../../../shared_modules/utils/flatbuffers/include/syscollector_deltas_generated.h"
#include "../../../../shared_modules/utils/flatbuffers/include/syscollector_deltas_schema.h"
#include "../../../../shared_modules/utils/flatbuffers/include/syscollector_synchronization_generated.h"
#include "../../../../shared_modules/utils/flatbuffers/include/syscollector_synchronization_schema.h"
#include "../scanOrchestrator/eventInsertInventory.hpp"
#include "MockOsDataCache.hpp"
#include "TrampolineOsDataCache.hpp"
#include "flatbuffers/flatbuffer_builder.h"
#include "flatbuffers/flatbuffers.h"
#include "flatbuffers/idl.h"
#include "json.hpp"

using ::testing::_;

namespace NSEventInsertInventoryTest
{
    constexpr auto TEST_INVENTORY_DATABASE_PATH {"queue/vd/inventory"};

    const std::string DELTA_PACKAGES_INSERTED_MSG =
        R"(
            {
                "agent_info": {
                    "agent_id": "001",
                    "agent_ip": "192.168.33.20",
                    "agent_name": "focal",
                    "node_name": "node01"
                },
                "data_type": "dbsync_packages",
                "data": {
                    "architecture": "amd64",
                    "checksum": "1e6ce14f97f57d1bbd46ff8e5d3e133171a1bbce",
                    "description": "library for GIF images library",
                    "format": "deb",
                    "groups": "libs",
                    "item_id": "ec465b7eb5fa011a336e95614072e4c7f1a65a53",
                    "multiarch": "same",
                    "name": "libgif7",
                    "priority": "optional",
                    "scan_time": "2023/08/04 19:56:11",
                    "size": 72,
                    "source": "giflib",
                    "vendor": "Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>",
                    "version": "5.1.9-1",
                    "install_time": "1577890801"
                },
                "operation": "INSERTED"
            }
        )";

    const std::string CVEID1 {"CVE-2024-5678"};
    const std::string CVEID2 {"CVE-2023-5362"};
} // namespace NSEventInsertInventoryTest

using namespace NSEventInsertInventoryTest;

void EventInsertInventoryTest::SetUp()
{
    m_inventoryDatabase = std::make_unique<Utils::RocksDBWrapper>(TEST_INVENTORY_DATABASE_PATH);
    for (const auto& element : AFFECTED_COMPONENT_COLUMNS)
    {
        if (!m_inventoryDatabase->columnExists(element.second))
        {
            m_inventoryDatabase->createColumn(element.second);
        }
    }

    if (!m_inventoryDatabase->columnExists(OS_INITIAL_SCAN))
    {
        m_inventoryDatabase->createColumn(OS_INITIAL_SCAN);
    }
}

void EventInsertInventoryTest::TearDown()
{
    spOsDataCacheMock.reset();
    m_inventoryDatabase->deleteAll();
    m_inventoryDatabase.reset();
    std::filesystem::remove_all(TEST_INVENTORY_DATABASE_PATH);
}

/*
 * @brief Test instantiation of the eventInsertInventory class.
 */
TEST_F(EventInsertInventoryTest, TestInstantiationOfTheeventInsertInventoryClass)
{
    // Instantiation of the eventInsertInventory class.
    EXPECT_NO_THROW(std::make_shared<TEventInsertInventory<TScanContext<TrampolineOsDataCache>>>(*m_inventoryDatabase));
}

/*
 * @brief Test handleRequest of the eventInsertInventory class (Package Insert Non Existing).
 */
TEST_F(EventInsertInventoryTest, TestHandleRequestPackageInsertNonExisting)
{
    // Instantiation of the eventInsertInventory class.
    auto eventInsertInventory =
        std::make_shared<TEventInsertInventory<TScanContext<TrampolineOsDataCache>>>(*m_inventoryDatabase);

    Os osData {.hostName = "osdata_hostname",
               .architecture = "osdata_architecture",
               .name = "osdata_name",
               .codeName = "osdata_codeName",
               .majorVersion = "osdata_majorVersion",
               .minorVersion = "osdata_minorVersion",
               .patch = "osdata_patch",
               .build = "osdata_build",
               .platform = "osdata_platform",
               .version = "osdata_version",
               .release = "osdata_release",
               .displayVersion = "osdata_displayVersion",
               .sysName = "osdata_sysName",
               .kernelVersion = "osdata_kernelVersion",
               .kernelRelease = "osdata_kernelRelease"};

    spOsDataCacheMock = std::make_shared<MockOsDataCache>();
    EXPECT_CALL(*spOsDataCacheMock, getOsData(_)).WillRepeatedly(testing::Return(osData));

    // Mock scanContext.
    flatbuffers::Parser parser;
    ASSERT_TRUE(parser.Parse(syscollector_deltas_SCHEMA));
    ASSERT_TRUE(parser.Parse(DELTA_PACKAGES_INSERTED_MSG.c_str()));
    uint8_t* buffer = parser.builder_.GetBufferPointer();
    std::variant<const SyscollectorDeltas::Delta*, const SyscollectorSynchronization::SyncMsg*, const nlohmann::json*>
        syscollectorDelta = SyscollectorDeltas::GetDelta(reinterpret_cast<const char*>(buffer));

    // Create a ScanContext object.
    auto scanContext = std::make_shared<TScanContext<TrampolineOsDataCache>>(syscollectorDelta);
    scanContext->m_elements[CVEID1] = nlohmann::json::object(); // Mock one vulnerability

    // Call handleRequest method.
    EXPECT_NO_THROW(eventInsertInventory->handleRequest(scanContext));

    auto& element = scanContext->m_elements[CVEID1];

    EXPECT_STREQ(element.at("operation").get_ref<const std::string&>().c_str(), "INSERTED");

    std::string inventoryEntry;
    EXPECT_TRUE(
        m_inventoryDatabase->get("node01_001_ec465b7eb5fa011a336e95614072e4c7f1a65a53", inventoryEntry, PACKAGE));

    EXPECT_STREQ(inventoryEntry.c_str(), CVEID1.c_str());
}

/*
 * @brief Test handleRequest of the eventInsertInventory class (Package Insert Already Existing).
 */
TEST_F(EventInsertInventoryTest, TestHandleRequestPackageInsertAlreadyExisting)
{
    // Instantiation of the eventInsertInventory class.
    m_inventoryDatabase->put("node01_001_ec465b7eb5fa011a336e95614072e4c7f1a65a53", CVEID2, PACKAGE);

    auto eventInsertInventory =
        std::make_shared<TEventInsertInventory<TScanContext<TrampolineOsDataCache>>>(*m_inventoryDatabase);

    Os osData {.hostName = "osdata_hostname",
               .architecture = "osdata_architecture",
               .name = "osdata_name",
               .codeName = "osdata_codeName",
               .majorVersion = "osdata_majorVersion",
               .minorVersion = "osdata_minorVersion",
               .patch = "osdata_patch",
               .build = "osdata_build",
               .platform = "osdata_platform",
               .version = "osdata_version",
               .release = "osdata_release",
               .displayVersion = "osdata_displayVersion",
               .sysName = "osdata_sysName",
               .kernelVersion = "osdata_kernelVersion",
               .kernelRelease = "osdata_kernelRelease"};

    spOsDataCacheMock = std::make_shared<MockOsDataCache>();
    EXPECT_CALL(*spOsDataCacheMock, getOsData(_)).WillRepeatedly(testing::Return(osData));

    // Mock scanContext.
    flatbuffers::Parser parser;
    ASSERT_TRUE(parser.Parse(syscollector_deltas_SCHEMA));
    ASSERT_TRUE(parser.Parse(DELTA_PACKAGES_INSERTED_MSG.c_str()));
    uint8_t* buffer = parser.builder_.GetBufferPointer();
    std::variant<const SyscollectorDeltas::Delta*, const SyscollectorSynchronization::SyncMsg*, const nlohmann::json*>
        syscollectorDelta = SyscollectorDeltas::GetDelta(reinterpret_cast<const char*>(buffer));

    // Create a ScanContext object.
    auto scanContext = std::make_shared<TScanContext<TrampolineOsDataCache>>(syscollectorDelta);
    scanContext->m_elements[CVEID1] = nlohmann::json::object(); // Mock one vulnerability

    // Call handleRequest method.
    EXPECT_NO_THROW(eventInsertInventory->handleRequest(scanContext));
    auto& element = scanContext->m_elements[CVEID1];

    EXPECT_STREQ(element.at("operation").get_ref<const std::string&>().c_str(), "INSERTED");

    std::string inventoryEntry;
    EXPECT_TRUE(
        m_inventoryDatabase->get("node01_001_ec465b7eb5fa011a336e95614072e4c7f1a65a53", inventoryEntry, PACKAGE));

    auto listCve = Utils::split(inventoryEntry, ',');
    EXPECT_EQ(listCve.size(), 2);
    for (const auto& key : listCve)
    {
        EXPECT_THAT(key, testing::AnyOf(testing::Eq(CVEID1.c_str()), testing::Eq(CVEID2.c_str())));
    }
}
